<?php
namespace db;

use Exception;
use ArrayObject;

/**
 * Implements common logic, so only fetch(), id(), count() needs to be implemented.
 * @author Jakub Dykowski
 *
 */
abstract class AbstractQueryResult implements QueryResult {
	/**
	 * Holds fetched rows. Do NOT use it directly outside self::addRow().
	 * @var \ArrayObject
	 */
	protected $rows = null;

	function __construct() {}
	
	/**
	 * Needs to be implemented. Will be called once, as soon as it will be needed. Do not call this method.
	 */
	protected abstract function fetch();

	function getIterator() {
		$this->ensureFetched();
		return new \ArrayIterator($this->rows);
	}

	function many() {
		$this->ensureFetched();
		return $this->rows;
	}

	function isEmpty() {
		return $this->count() < 1;
	}

	function one() {
		if (($count = $this->count()) > 1)
			throw new DatabaseException("query returned more than ONE result");
		
		$this->ensureFetched();
		return $count ? $this->rows[0] : null;
	}

	function field($name = null) {
		$entity = $this->one();
		if ($entity === null)
			throw new DatabaseException("result is empty, cannot get it's field");
		return $name ? $entity[$name] : $entity[0];
	}

	function keyValue($key, $value) {
		$result = array ();
		foreach ($this->many() as $row) {
			if (!isset($row[$key]))
				throw new DatabaseException("key '$key' not found in query result");
			if (!isset($row[$value]))
				throw new DatabaseException("key '$value' not found in query result");
			if (isset($result[$row[$key]]))
				throw new DatabaseException("key '$value' isn't unique");
			$result[$row[$key]] = $row[$value];
		}
		return $result;
	}

	function group($by) {
		$args = func_get_args();
		if (count($args) < 1)
			throw new InvalidArgumentException("at least one field expected to group by");
		
		$toGroup = $this->many();
		return $this->groupArray($toGroup, $args);
	}

	protected static function groupArray(&$toGroup, $args) {
		$by = array_shift($args);
		$grouped = array ();
		foreach ($toGroup as &$row) {
			$key = $row[$by];
			if (!isset($key))
				throw new DatabaseException("group key '$by' not found in query result");
			$grouped[$key][] = $row;
		}
		if (count($args))
			foreach ($grouped as $key => &$row)
				$grouped[$key] = static::groupArray($grouped[$key], $args);
		
		return $grouped;
	}

	/**
	 * Ensures that query result is fetched, may fetch when called.
	 */
	protected function ensureFetched() {
		if (count($this->rows) < $this->count()) {
			if ($this->rows == null)
				$this->rows = array ();
			$this->fetch();
		} else if ($this->count() == 0 and $this->rows === null)
			$this->rows = array ();
	}
	
	/**
	 * Need to be called inside fetch() do add fetched row to result.
	 * @param unknown $row
	 */
	protected function addRow($row) {
		$this->rows[] = new ArrayObject($row, ArrayObject::ARRAY_AS_PROPS);
	}
}